package com.block.match.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.block.channel.enums.ChannelTypeEnum;
import com.block.common.constant.AppRspCodeConstant;
import com.block.common.constant.RedisKeyConstant;
import com.block.common.enums.InfoTypeEnum;
import com.block.common.exception.NotifyExceptionService;
import com.block.common.model.dto.CountInfoDTO;
import com.block.common.service.IApplicationInfoService;
import com.block.common.service.IChannelCommonService;
import com.block.common.service.IMerchantCommonService;
import com.block.common.service.LogFactory;
import com.block.common.service.merchant.IMerchantThirdBaseInfoService;
import com.block.common.util.LocalDateUtil;
import com.block.common.vo.ResponseVo;
import com.block.datapush.factory.PushExtDataFactory;
import com.block.datapush.model.ProtocolInfoEntity;
import com.block.db.entity.*;
import com.block.db.mapper.ChannelBaseInfoMapper;
import com.block.db.mapper.DataMatchOrderItemMapper;
import com.block.db.mapper.MemberSourceInfoMapper;
import com.block.db.mapper.MerchantBaseInfoMapper;
import com.block.match.constant.DataMatchConstant;
import com.block.match.enums.OrderStatusEnum;
import com.block.match.model.dto.MatchOrderResultDTO;
import com.block.match.model.vo.MatchOrderItem;
import com.block.match.model.vo.MatchOrderVO;
import com.block.match.model.vo.MatchResultVO;
import com.block.match.service.IDataMatchOrderInfoService;
import com.block.match.service.IMatchApiBHandlerService;
import com.block.match.service.IMatchHandleService;
import com.block.merchant.model.dto.MerchantOrderDTO;
import com.block.merchant.model.result.DataMatchResult;
import com.block.merchant.model.result.MatchMerchantInfo;
import com.block.merchant.model.vo.MerchantAbiBDataPushInfoVO;
import com.block.merchant.service.IDataMerchantPushService;
import com.block.merchant.service.IMerchantBaseInfoService;
import com.block.merchant.service.IMerchantTaskInfoService;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.*;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

@Service
public class MatchHandleService implements IMatchHandleService {

    private static final Logger log = LogFactory.getCommLog();

    @Autowired
    NotifyExceptionService notifyExceptionService;
    @Autowired
    IDataMatchOrderInfoService dataMatchOrderInfoService;
    @Autowired
    IDataMerchantPushService dataMerchantPushService;
    @Autowired
    IMerchantCommonService merchantCommonService;
    @Resource
    MemberSourceInfoMapper memberSourceInfoMapper;
    @Autowired
    IMerchantTaskInfoService merchantTaskInfoService;
    @Autowired
    IMerchantBaseInfoService merchantBaseInfoService;
    @Resource
    DataMatchOrderItemMapper dataMatchOrderItemMapper;
    @Autowired
    IApplicationInfoService applicationInfoService;
    @Autowired
    IMerchantThirdBaseInfoService merchantThirdBaseInfoService;


    @Autowired
    IChannelCommonService channelCommonService;
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    IMatchApiBHandlerService matchApiBHandlerService;
    @Autowired
    ChannelBaseInfoMapper channelBaseInfoMapper;
    @Autowired
    IDataMatchOrderInfoService iDataMatchOrderInfoService;

    /**
     * 用户信息匹配
     * <p>
     * 每个进来的用户都会产生一个匹配记录
     */
//    @Override
//    public MatchResultVO getMatchResult(MemberSourceInfo memberSourceInfo) {
//
//        try {
//            CountDownLatch countDownLatch = new CountDownLatch(1);
//            Future<MatchResultVO> merchantInfoFuture = ThreadUtil.execAsync(new Callable<MatchResultVO>(){
//                @Override
//                public MatchResultVO call() throws Exception {
//
//                    MatchResultVO matchResultVO = getMatchResult0(memberSourceInfo);
//                    countDownLatch.countDown();
//                    return matchResultVO;
//                }
//            });
//
//            // 撞库时间
//            countDownLatch.await(4000, TimeUnit.MILLISECONDS);
//
//            if (merchantInfoFuture.isDone()){
//                MatchResultVO matchResultVO = merchantInfoFuture.get();
//                return matchResultVO;
//            }
//
////            System.out.println("撞库超时=====================");
//
//        }catch (Exception exception){
//            exception.printStackTrace();
//            String message = StrUtil.format("【channelCode】{}【channelName】{}【uid】{}",
//                    memberSourceInfo.getChannelCode(), memberSourceInfo.getChannelName(), memberSourceInfo.getUid());
//            notifyExceptionService.notifyNoticeInfo("MEMBER_CHECK_EXC", "撞库操作异常", message, exception);
//
//        }
//        return null;
//    }


    @Override
    public List<MatchResultVO> getMatchResult0(MemberSourceInfo memberSourceInfo,String type) {
        try {
            /**
             * 初始化匹配订单信息
             */
            DataMatchOrderInfo dataMatchOrderInfo = dataMatchOrderInfoService.initMatchOrderInfo(memberSourceInfo);

            List<MatchResultVO> matchResultVOList  = new ArrayList<>();

            //matchResultVO.setOrderId(String.valueOf(dataMatchOrderInfo.getId()));
            //设置过期时间
            //撞库完成后设置用户再次能够撞库的的时间
            long ttl = getSecondsUntilTomorrow();
            String nowdate = LocalDate.now().toString();
            String redisKey = StrUtil.format(RedisKeyConstant.MERCHANT_DATE_UID ,nowdate,memberSourceInfo.getUid());
            stringRedisTemplate.opsForValue().set(redisKey, String.valueOf(dataMatchOrderInfo.getId()), ttl, TimeUnit.SECONDS);

            // 条件过滤
            DataMatchResult dataMatchResult = dataMerchantPushService.dataMatchHandle(memberSourceInfo,type);
            if (dataMatchResult.getMatchSize() == 0) {
                MatchResultVO matchResultVO = new MatchResultVO();
                matchResultVO.setMatchSuccess(false);
                matchResultVOList.add(matchResultVO);

                DataMatchOrderInfo updateOrder = new DataMatchOrderInfo();
                updateOrder.setId(dataMatchOrderInfo.getId());
                updateOrder.setStatus(OrderStatusEnum.FAILED.getValue());
                updateOrder.setMessage("没有匹配到合适的商户信息");
                dataMatchOrderInfoService.updateById(updateOrder);

                log.info("【客户信息匹配】匹配完成，没有匹配到合适的商户【orderId】{}", dataMatchOrderInfo.getId());


                return matchResultVOList;
            }

            dataMatchResult.setMatchOrderId(dataMatchOrderInfo.getId());
            dataMatchResult.setUid(memberSourceInfo.getUid());

            /**
             * 根据匹配结果，更新匹配订单信息
             */
            List<MatchOrderResultDTO> resultDTOList =
                    dataMatchOrderInfoService.saveMatchOrderResult(dataMatchResult, dataMatchOrderInfo, memberSourceInfo);

            /**
             * 撞库成功总计
             */
            CountInfoDTO countInfoDTO = new CountInfoDTO();
            countInfoDTO.setChannelCode(memberSourceInfo.getChannelCode());
            if (resultDTOList != null && !resultDTOList.isEmpty()) {
                countInfoDTO.setMatchSuccessCount(1);
                countInfoDTO.setMatchSuccessNumber(resultDTOList.size());
            }else {
                countInfoDTO.setWuYongCount(1);
            }
            channelCommonService.updateChannelCount(countInfoDTO);



            // 组装返回信息

//            matchResultVO.setMatchSuccess(true);
//            matchResultVO.setOrderProtocolUrl(StrUtil.format(DataMatchConstant.PAGE_APPROVE_URI, dataMatchOrderInfo.getId()));
//            List<MatchOrderItem> orderItemList = new ArrayList<>();
//            for (MatchOrderResultDTO orderResultDTO : resultDTOList) {
//                MatchOrderItem matchOrderItem = new MatchOrderItem();
//                matchOrderItem.setOrderItemId(String.valueOf(orderResultDTO.getOrderItemId()));
//
//                MatchMerchantInfo matchMerchantInfo = orderResultDTO.getMatchMerchantInfo();
//                matchOrderItem.setInfoType(matchMerchantInfo.getInfoType());
//                matchOrderItem.setApproveFlag(matchMerchantInfo.getApproveFlag());
//                matchOrderItem.setApproveUrl(matchMerchantInfo.getApproveUrl());
//                matchOrderItem.setProtocolUrl(matchMerchantInfo.getProtocolUrl());
//
//                // 查询机构信息
//                MerchantBaseInfo merchantBaseInfo = merchantCommonService.getMerchantBaseInfo(matchMerchantInfo.getInfoId());
//                matchOrderItem.setCompanyName(merchantBaseInfo.getCompanyName());
//                matchOrderItem.setCompanyShortName(merchantBaseInfo.getCompanyShortName());
//                matchOrderItem.setCompanyTitle(merchantBaseInfo.getCompanyTitle());
//                matchOrderItem.setCompanySubTitle(merchantBaseInfo.getCompanySubTitle());
//
//                matchOrderItem.setCompanyLogo(merchantBaseInfo.getCompanyLogo());
//                matchOrderItem.setMaxLoanAmount(merchantBaseInfo.getMaxLoanAmount());
//                matchOrderItem.setMaxRate(merchantBaseInfo.getMaxRate());
//                matchOrderItem.setMinRate(merchantBaseInfo.getMinRate());
//
//                matchResultVO.setMatchSuccess(true);
//                matchResultVO.setOrderProtocolUrl(StrUtil.format(DataMatchConstant.PAGE_APPROVE_URI, dataMatchOrderInfo.getId()));
//
//                orderItemList.add(matchOrderItem);
//            }
//            matchResultVO.setOrderItemList(orderItemList);


            for (MatchOrderResultDTO orderResultDTO : resultDTOList) {
                MatchResultVO matchResultVO = new MatchResultVO();
                matchResultVO.setMatchSuccess(true);
                matchResultVO.setOrderId(String.valueOf(dataMatchOrderInfo.getId()));
                if(StrUtil.equals(dataMatchOrderInfo.getAppCode(),"anyirong_mini") || StrUtil.equals(dataMatchOrderInfo.getAppCode(),"anyirongh5")
                || StrUtil.equals(dataMatchOrderInfo.getAppCode(),"anyirong_app")){
                    matchResultVO.setOrderProtocolUrl(StrUtil.format(DataMatchConstant.PAGE_APPROVE_URI, dataMatchOrderInfo.getId()));
                }
                if(StrUtil.equals(dataMatchOrderInfo.getAppCode(),"hongchang_mini")) {
                    matchResultVO.setOrderProtocolUrl(StrUtil.format(DataMatchConstant.PAGE_APPROVE_URI_HONG, dataMatchOrderInfo.getId()));
                }
                if(StrUtil.equals(dataMatchOrderInfo.getAppCode(),"hengxiaorong_mini")) {
                    matchResultVO.setOrderProtocolUrl(StrUtil.format(DataMatchConstant.PAGE_APPROVE_URI_RONG, dataMatchOrderInfo.getId()));
                }
                if(StrUtil.equals(dataMatchOrderInfo.getAppCode(),"rongxiaoxin_mini")){
                    matchResultVO.setOrderProtocolUrl(StrUtil.format(DataMatchConstant.PAGE_APPROVE_URI_XIN, dataMatchOrderInfo.getId()));
                }
                if(StrUtil.equals(dataMatchOrderInfo.getAppCode(),"yixiaorong_mini")){
                    matchResultVO.setOrderProtocolUrl(StrUtil.format(DataMatchConstant.PAGE_APPROVE_URI_YI, dataMatchOrderInfo.getId()));
                }
                if(StrUtil.equals(dataMatchOrderInfo.getAppCode(),"rongrongle_mini")){
                    matchResultVO.setOrderProtocolUrl(StrUtil.format(DataMatchConstant.PAGE_APPROVE_URI_LE, dataMatchOrderInfo.getId()));
                }
                if(StrUtil.equals(dataMatchOrderInfo.getAppCode(),"beitun_mini")){
                    matchResultVO.setOrderProtocolUrl(StrUtil.format(DataMatchConstant.PAGE_APPROVE_URI_BEI, dataMatchOrderInfo.getId()));
                }
                if(StrUtil.equals(dataMatchOrderInfo.getAppCode(),"xinhui_mini") || StrUtil.equals(dataMatchOrderInfo.getAppCode(),"anyxhh5")){
                    matchResultVO.setOrderProtocolUrl(StrUtil.format(DataMatchConstant.PAGE_APPROVE_URI_HUI, dataMatchOrderInfo.getId()));
                }
                if(StrUtil.equals(dataMatchOrderInfo.getAppCode(),"anyjxh5")){
                    matchResultVO.setOrderProtocolUrl(StrUtil.format(DataMatchConstant.PAGE_APPROVE_URI_JU, dataMatchOrderInfo.getId()));
                }
                if(StrUtil.equals(dataMatchOrderInfo.getAppCode(),"bldd_mini")){
                    matchResultVO.setOrderProtocolUrl(StrUtil.format(DataMatchConstant.PAGE_APPROVE_URI_BAO, dataMatchOrderInfo.getId()));
                }
                if(StrUtil.equals(dataMatchOrderInfo.getAppCode(),"rongxiaohe_mini")){
                    matchResultVO.setOrderProtocolUrl(StrUtil.format(DataMatchConstant.PAGE_APPROVE_URI_HE, dataMatchOrderInfo.getId()));
                }
                List<MatchOrderItem> orderItemList = new ArrayList<>();
                MatchMerchantInfo matchMerchantInfo = orderResultDTO.getMatchMerchantInfo();
                MatchOrderItem matchOrderItem = new MatchOrderItem();
                List<Map<String, String>> moreProtocolUrl = new ArrayList<>();
                ApplicationInfo appInfoByAppCode = applicationInfoService.getAppInfoByAppCode(dataMatchOrderInfo.getAppCode());
                if(StrUtil.equals(matchMerchantInfo.getInfoId().toString(),"305")){
                    HashMap<String, String> map = new HashMap<>();
                    map.put("name","八戒金服-个人信息共享协议");
                    map.put("url",StrUtil.format(DataMatchConstant.PAGE_APPROVE_URI_BA,appInfoByAppCode.getApiUrl(), memberSourceInfo.getUid()));
                    moreProtocolUrl.add(map);
                    matchResultVO.setMoreProtocolUrl(moreProtocolUrl);
                }
                if(StrUtil.equals(matchMerchantInfo.getInfoId().toString(),"320")){
                    HashMap<String, String> map = new HashMap<>();
                    map.put("name","易优花-个人信息共享协议");
                    map.put("url",StrUtil.format(DataMatchConstant.PAGE_APPROVE_URI_YOU,appInfoByAppCode.getApiUrl(), memberSourceInfo.getUid()));
                    moreProtocolUrl.add(map);
                    matchResultVO.setMoreProtocolUrl(moreProtocolUrl);
                }

                //matchResultVO.setOrderProtocolUrl(StrUtil.format(DataMatchConstant.PAGE_APPROVE_URI, dataMatchOrderInfo.getId()));

//                List<MatchOrderItem> orderItemList = new ArrayList<>();
//                MatchOrderItem matchOrderItem = new MatchOrderItem();
                matchOrderItem.setOrderItemId(String.valueOf(orderResultDTO.getOrderItemId()));

                //MatchMerchantInfo matchMerchantInfo = orderResultDTO.getMatchMerchantInfo();
                matchOrderItem.setInfoType(matchMerchantInfo.getInfoType());
                matchOrderItem.setApproveFlag(matchMerchantInfo.getApproveFlag());
                matchOrderItem.setApproveUrl(matchMerchantInfo.getApproveUrl());
                matchOrderItem.setProtocolUrl(matchMerchantInfo.getProtocolUrl());

                // 查询机构信息
                MerchantBaseInfo merchantBaseInfo = merchantCommonService.getMerchantBaseInfo(matchMerchantInfo.getInfoId());
                if (merchantBaseInfo != null) {
                    matchOrderItem.setCompanyName(merchantBaseInfo.getCompanyName());
                    matchOrderItem.setCompanyShortName(merchantBaseInfo.getCompanyShortName());
                    matchOrderItem.setCompanyTitle(merchantBaseInfo.getCompanyTitle());
                    matchOrderItem.setCompanySubTitle(merchantBaseInfo.getCompanySubTitle());
                    matchOrderItem.setCompanyLogo(merchantBaseInfo.getCompanyLogo());
                    matchOrderItem.setMaxLoanAmount(merchantBaseInfo.getMaxLoanAmount());
                    matchOrderItem.setMaxRate(merchantBaseInfo.getMaxRate());
                    matchOrderItem.setMinRate(merchantBaseInfo.getMinRate());
                    matchOrderItem.setSublabelInfo(merchantBaseInfo.getSublabelInfo());
                    matchOrderItem.setInfoId(merchantBaseInfo.getId());
                    orderItemList.add(matchOrderItem);
                }

            matchResultVO.setOrderItemList(orderItemList);
            matchResultVOList.add(matchResultVO);

            Long remainSecondNumber = LocalDateUtil.getRemainSecondNumber();
            String redisKeys = StrUtil.format(RedisKeyConstant.MERCHANT_PUSH_UID_ITEMID ,memberSourceInfo.getUid(),matchOrderItem.getOrderItemId());
            stringRedisTemplate.opsForValue().set(redisKeys,JSON.toJSONString(matchResultVO),remainSecondNumber,TimeUnit.SECONDS);

            }
            return matchResultVOList;
        } catch (Exception exception) {
            exception.printStackTrace();
            notifyExceptionService.notifyNoticeInfo("MATCH_MERCHANT", "资质匹配异常", "uid:"+memberSourceInfo.getUid(), exception);
        }
        return null;
    }

    @Override
    public ResponseVo getMatchOrderInfo(String orderNo) {

        DataMatchOrderInfo matchOrderInfo = dataMatchOrderInfoService.getById(orderNo);
        if (ObjectUtil.isEmpty(matchOrderInfo)){
            return new ResponseVo(AppRspCodeConstant.DATA_INFO_NULL, "订单信息为空");
        }

        List<DataMatchOrderItem> orderItemList = dataMatchOrderItemMapper.getEntityListByOrderNoExt(orderNo);
        if (CollUtil.isEmpty(orderItemList)){
            return new ResponseVo(AppRspCodeConstant.DATA_INFO_NULL, "子订单信息为空");
        }

        DataMatchOrderItem matchOrderItem = orderItemList.get(0);

        MatchOrderVO matchOrderVO = new MatchOrderVO();
        matchOrderVO.setOrderNo(orderNo);
        matchOrderVO.setOrderItemNo(String.valueOf(matchOrderItem.getId()));


        ApplicationInfo applicationInfo = applicationInfoService.getAppInfoByAppCode(matchOrderInfo.getAppCode());

        // 平台协议地址
        String ownerProtocolUrl = StrUtil.format("{}page/newprotocol/{}/{}.html", applicationInfo.getApiUrl(), matchOrderVO.getOrderNo(), matchOrderVO.getOrderItemNo());

        MerchantBaseInfo merchantBaseInfo = merchantBaseInfoService.getById(matchOrderItem.getInfoId());
        MemberSourceInfo memberSourceInfo = memberSourceInfoMapper.selectById(matchOrderItem.getUid());
        JSONObject extObject = new JSONObject();
        extObject.put("orderNo", orderNo);
        extObject.put("orderItemNo", String.valueOf(matchOrderItem.getId()));

        // 机构协议地址
        // 如果机构没有协议地址，则使用平台的协议地址
        List<ProtocolInfoEntity> protocolList = PushExtDataFactory.getService(merchantBaseInfo.getFactoryType())
                .getProtocolUrl(merchantBaseInfo, memberSourceInfo, extObject);
        if (CollUtil.isEmpty(protocolList)) {
            ProtocolInfoEntity infoEntity = new ProtocolInfoEntity();
            infoEntity.setProtocolUrl(ownerProtocolUrl);
            infoEntity.setProtocolName("授权协议");
            protocolList.add(infoEntity);
        }

        matchOrderVO.setOwnerProtocolUrl(ownerProtocolUrl);
        matchOrderVO.setOtherProtocolUrl(protocolList);


        if (StrUtil.equals(merchantBaseInfo.getInfoType(), InfoTypeEnum.APIS.getValue())){
            // 查询
            String pushId = matchOrderItem.getPushId();
            MerchantThirdBaseInfo thirdBaseInfo = merchantThirdBaseInfoService.selectEntityByOrderNo(pushId);
            matchOrderVO.setCompantName( thirdBaseInfo.getCompanyName());
            matchOrderVO.setCompantShortName( thirdBaseInfo.getProductName());
            matchOrderVO.setCompantTitle( thirdBaseInfo.getBusinessInfo());
            matchOrderVO.setCompantSubTitle( thirdBaseInfo.getBusinessInfo());
            matchOrderVO.setCompantLogo(thirdBaseInfo.getCompanyLogo());
            matchOrderVO.setMaxLoanAmount( thirdBaseInfo.getMaxLoanAmount());
            matchOrderVO.setMaxRate( thirdBaseInfo.getMaxLoanRate());
            matchOrderVO.setMinRate( thirdBaseInfo.getMinLoanRate());
        }else{
            matchOrderVO.setCompantName( merchantBaseInfo.getCompanyName());
            matchOrderVO.setCompantShortName( merchantBaseInfo.getCompanyShortName());
            matchOrderVO.setCompantTitle( merchantBaseInfo.getCompanyTitle());
            matchOrderVO.setCompantSubTitle(merchantBaseInfo.getCompanySubTitle());
            matchOrderVO.setCompantLogo( merchantBaseInfo.getCompanyLogo());
            matchOrderVO.setMaxLoanAmount(merchantBaseInfo.getMaxLoanAmount());
            matchOrderVO.setMaxRate(merchantBaseInfo.getMaxRate());
            matchOrderVO.setMinRate( merchantBaseInfo.getMinRate());
        }

        return new ResponseVo(AppRspCodeConstant.SUCCESS, matchOrderVO);
    }

    /**
     * 处理商户的手动推送
     * @param infoId
     * @param uid
     * @return
     */
    @Override
    public ResponseVo handlePushManual(String infoId, String uid){
        try {
            // 获取可推送的任务列表
            MemberSourceInfo memberSourceInfo = memberSourceInfoMapper.selectById(Long.valueOf(uid));
            List<MerchantTaskInfo> pushTaskList = merchantTaskInfoService.getMerchantCanPushTaskList(infoId, memberSourceInfo);
            if (ObjectUtil.isEmpty(pushTaskList)){
                return new ResponseVo(AppRspCodeConstant.SYS_EXCEPTION, "无满足该条数据的推送任务（包括账户余额不足）");
            }

            MerchantTaskInfo merchantTaskInfo = pushTaskList.get(0);

            MerchantOrderDTO merchantOrderDTO = new MerchantOrderDTO();
            merchantOrderDTO.setInfoId(merchantTaskInfo.getInfoId());
            merchantOrderDTO.setTaskId(merchantTaskInfo.getId());
            merchantOrderDTO.setPriceAmount(merchantTaskInfo.getPriceAmount());
            merchantOrderDTO.setCostAmount(merchantTaskInfo.getCostAmount());
            merchantOrderDTO.setPayAmount(merchantTaskInfo.getPayAmount());

            MatchMerchantInfo matchMerchantInfo = merchantBaseInfoService.matchWithMerchant(merchantOrderDTO, memberSourceInfo);
            if (!matchMerchantInfo.isMatchSuccess()){
                return new ResponseVo(AppRspCodeConstant.SYS_EXCEPTION, "撞库失败；不可以推送");
            }

            // 保存 推送订单以及推送订单 子信息
            // 并推送
            boolean b = dataMatchOrderInfoService.handleManualToPush(memberSourceInfo, merchantTaskInfo, matchMerchantInfo);
            if (b){
                return new ResponseVo(AppRspCodeConstant.SUCCESS);
            }
            return new ResponseVo(AppRspCodeConstant.SYS_EXCEPTION, "推送失败");

        } catch (Exception exception) {
            exception.printStackTrace();
            notifyExceptionService.notifyNoticeInfo("MERCHANT_PUSH_MANUAL", "商户数据手动推送", "uid:"+uid+";infoId:"+infoId, exception);
        }
        return new ResponseVo(AppRspCodeConstant.SYS_EXCEPTION, "推送异常");
    }

    @Override
    public ResponseVo PushApBManual(String infoId, String uid) {
        try {
            // 获取可推送的任务列表
            MemberSourceInfo memberSourceInfo = memberSourceInfoMapper.selectById(Long.valueOf(uid));


            List<MerchantTaskInfo> pushTaskList = merchantTaskInfoService.getMerchantCanPushTaskList(infoId, memberSourceInfo);
            if (ObjectUtil.isEmpty(pushTaskList)){
                return new ResponseVo(AppRspCodeConstant.SYS_EXCEPTION, "无满足该条数据的推送任务（包括账户余额不足）");
            }

            MerchantTaskInfo merchantTaskInfo = pushTaskList.get(0);

            MerchantOrderDTO merchantOrderDTO = new MerchantOrderDTO();
            merchantOrderDTO.setInfoId(merchantTaskInfo.getInfoId());
            merchantOrderDTO.setTaskId(merchantTaskInfo.getId());
            merchantOrderDTO.setPriceAmount(merchantTaskInfo.getPriceAmount());
            merchantOrderDTO.setCostAmount(merchantTaskInfo.getCostAmount());
            merchantOrderDTO.setPayAmount(merchantTaskInfo.getPayAmount());

            MatchMerchantInfo matchMerchantInfo = merchantBaseInfoService.matchWithMerchant(merchantOrderDTO, memberSourceInfo);
            if (!matchMerchantInfo.isMatchSuccess()){
                return new ResponseVo(AppRspCodeConstant.SYS_EXCEPTION, "撞库失败；不可以推送");
            }

            // 保存 推送订单以及推送订单 子信息
            // 并推送
            MerchantAbiBDataPushInfoVO merchantAbiBDataPushInfoVO = dataMatchOrderInfoService.manualApiBPush(memberSourceInfo, merchantTaskInfo, matchMerchantInfo);
            if (merchantAbiBDataPushInfoVO.getStatus()){
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("merchantAbiBDataPushInfoVO",merchantAbiBDataPushInfoVO);
                return new ResponseVo(AppRspCodeConstant.SUCCESS,jsonObject);
            }
            return new ResponseVo(AppRspCodeConstant.SYS_EXCEPTION, "推送失败");

        } catch (Exception exception) {
            exception.printStackTrace();
            notifyExceptionService.notifyNoticeInfo("MERCHANT_PUSH_MANUAL", "商户数据手动推送", "uid:"+uid+";infoId:"+infoId, exception);
        }
        return new ResponseVo(AppRspCodeConstant.SYS_EXCEPTION, "推送异常");
    }

    @Override
    public ResponseVo againMatchMerchant(Long uid,String channelCode) {

        // 1. 检查当日是否已存在撞库结果
        String today = LocalDate.now().toString();
        String redisTodayKey = StrUtil.format(RedisKeyConstant.MERCHANT_DATE_UID, today, uid);
        MemberSourceInfo memberSourceInfo = getMemberSourceInfo(String.valueOf(uid));
        ChannelBaseInfo channelBaseInfo = channelBaseInfoMapper.getBaseInfoByCode(channelCode);
        memberSourceInfo.setAppCode(channelBaseInfo.getAppCode());
        memberSourceInfo.setChannelCode(channelBaseInfo.getChannelCode());
        memberSourceInfo.setAppName(channelBaseInfo.getAppName());
        memberSourceInfo.setChannelName(channelBaseInfo.getChannelName());
        if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(redisTodayKey))) {
            List<MatchResultVO> cachedResults = getCachedMatchResults(String.valueOf(uid));
            // redis没有查询到匹配机构直接false
            if (cachedResults.isEmpty()) {
                return new ResponseVo(AppRspCodeConstant.SUCCESS, Collections.singletonList(createEmptyMatchResult(false)));
            }
            //App走进件 异步执行
            if (StrUtil.equals(channelBaseInfo.getPageType(), "app")) {
                processAppEntryAsync(memberSourceInfo, cachedResults);
            }
            return new ResponseVo(AppRspCodeConstant.SUCCESS, cachedResults);
        }
        // 3. 首次处理逻辑
        if(!StrUtil.equals(channelBaseInfo.getChannelType(), ChannelTypeEnum.TEST.getValue())) {

            List<MatchResultVO> initialResults = processInitialMatching(memberSourceInfo,channelBaseInfo);

            // 4. 触发所有机构推送
            triggerAllMerchantPush(String.valueOf(uid));

            return new ResponseVo(AppRspCodeConstant.SUCCESS, initialResults);
        }
        return new ResponseVo(AppRspCodeConstant.SUCCESS);
    }


    private List<MatchResultVO> getCachedMatchResults(String uid) {
        String pattern = StrUtil.format(RedisKeyConstant.MERCHANT_PUSH_UID_ITEMID, uid, "*");
        List<MatchResultVO> results = new ArrayList<>();

        try (Cursor<byte[]> cursor = stringRedisTemplate.execute(
                (RedisCallback<Cursor<byte[]>>) conn -> conn.scan(
                        ScanOptions.scanOptions()
                                .match(pattern)
                                .count(1000)
                                .build()))
        ) {

            List<String> keys = new ArrayList<>();
            while (cursor.hasNext()) {
                keys.add(new String(cursor.next()));
                if (keys.size() >= 100) {
                    processBatchKeys(keys, results);
                    keys.clear();
                }
            }
            if (!keys.isEmpty()) {
                processBatchKeys(keys, results);
            }
        }

        return results.stream()
                .peek(vo -> {
                    if (vo.getOrderItemList() != null) {
                        vo.getOrderItemList().sort(Comparator.comparing(MatchOrderItem::getOrderItemId));
                    }
                })
                .sorted(Comparator.comparing(
                        vo -> vo.getOrderItemList().isEmpty() ? "" : vo.getOrderItemList().get(0).getOrderItemId(),
                        Comparator.nullsFirst(Comparator.naturalOrder())))
                .collect(Collectors.toList());
    }

    private void processBatchKeys(List<String> keys, List<MatchResultVO> results) {
        List<String> values = stringRedisTemplate.opsForValue().multiGet(keys);
        if (values != null) {
            for (String value : values) {
                if (StrUtil.isNotBlank(value)) {
                    MatchResultVO vo = JSON.parseObject(value, MatchResultVO.class);
                    vo.setMatchSuccess(true);
                    results.add(vo);
                }
            }
        }
    }

    private MemberSourceInfo getMemberSourceInfo(String uid) {
        MemberSourceInfo info = memberSourceInfoMapper.selectById(uid);
        if (info == null) {
            throw new RuntimeException("用户不存在: " + uid);
        }
        return info;
    }

    private List<MatchResultVO> processInitialMatching(MemberSourceInfo member,ChannelBaseInfo channelBaseInfo) {
        //选择机构推送type
        String type = "2";
        List<MatchResultVO> results = this.getMatchResult0(member,type);
        //App异步进件
//        if(StrUtil.equals(channelBaseInfo.getPageType(), "app") && results != null) {
//            processAppEntryAsync(member,results);
//        }
        return results.isEmpty() ?
                Collections.singletonList(createEmptyMatchResult(false)) :
                results;
    }

    @Async
    public void processAppEntryAsync(MemberSourceInfo member, List<MatchResultVO> results) {
        try {
            String orderId = extractOrderId(results);
            String orderItemIds = extractOrderItemIds(results);

            // 异步进件
            iDataMatchOrderInfoService.approveToPush(member.getUid(), orderId, orderItemIds, "");

            log.info("App无感进件异步处理完成, uid: {}, orderItemIds: {}", member.getUid(), orderItemIds);
        } catch (Exception e) {
            log.error("App无感进件异步处理异常, uid: {}", member.getUid(), e);
        }
    }
    private String extractOrderId(List<MatchResultVO> results) {
        return results.stream()
                .filter(Objects::nonNull)
                .map(MatchResultVO::getOrderId)
                .filter(StrUtil::isNotBlank)
                .findFirst()
                .orElse("");
    }

    private String extractOrderItemIds(List<MatchResultVO> results) {
        return JSON.toJSONString(
                results.stream()
                        .flatMap(result -> result.getOrderItemList().stream())
                        .map(MatchOrderItem::getOrderItemId)
                        .filter(StrUtil::isNotBlank)
                        .collect(Collectors.toList()));
    }

    private void triggerAllMerchantPush(String uid) {
        matchApiBHandlerService.getApiBMatchResult(Long.valueOf(uid));
    }

    private MatchResultVO createEmptyMatchResult(boolean success) {
        MatchResultVO result = new MatchResultVO();
        result.setMatchSuccess(success);
        return result;
    }

    public static long getSecondsUntilTomorrow() {
        Calendar now = Calendar.getInstance();
        Calendar tomorrow = (Calendar) now.clone();
        tomorrow.add(Calendar.DAY_OF_MONTH, 1);
        tomorrow.set(Calendar.HOUR_OF_DAY, 0);
        tomorrow.set(Calendar.MINUTE, 0);
        tomorrow.set(Calendar.SECOND, 0);
        tomorrow.set(Calendar.MILLISECOND, 0);

        return (tomorrow.getTimeInMillis() - now.getTimeInMillis()) / 1000;
    }

}
